package study.web.security;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import lombok.extern.slf4j.Slf4j;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import study.domain.Constants;
import study.domain.user.User;
import study.service.user.UserService;

/**
 * 보안과 관련된 처리 지원
 *
 * @author Barney Kim
 */
@Slf4j
@Service
public class SecuritySupport {

	@Resource(name = "userService")
	private UserService userService;

	@Resource(name = "userDetailsService")
	private UserDetailsService userDetailsService;

	public String getUsername() {
		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
		return getUsername(auth);
	}

	/**
	 * 인증에서 사용자명을 반환한다.
	 *
	 * @param authentication
	 *            인증
	 * @return 사용자명
	 */
	public String getUsername(Authentication authentication) {
		Object principal = authentication.getPrincipal();
		if (principal instanceof UserDetails) {
			return ((UserDetails) principal).getUsername();
		} else {
			return principal.toString();
		}
	}

	/**
	 * 사용자명으로 사용자를 찾아 Spring Security 컨텍스트에 인증을 추가 한다.
	 *
	 * @param username
	 *            사용자명
	 */
	public void setAuthentication(String username) {
		UserDetails userDetails = userDetailsService.loadUserByUsername(username);
		Authentication auth = new UsernamePasswordAuthenticationToken(
				userDetails.getUsername(), userDetails.getPassword(),
				userDetails.getAuthorities());
		log.debug("auth={}", auth);
		SecurityContextHolder.getContext().setAuthentication(auth);
	}

	/**
	 * 현재 쓰레드에 연결된 요청의 세션에 사용자를 넣는다.
	 *
	 * @param username
	 */
	public void setSessionUser(String username) {
		setSessionUser(RequestContextHolder.currentRequestAttributes(), username);
	}

	/**
	 * 요청의 세션에 사용자를 넣는다.
	 *
	 * @param requestAttributes
	 * @param username
	 */
	public void setSessionUser(RequestAttributes requestAttributes, String username) {
		HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();
		setSessionUser(request, username);
	}

	/**
	 * 요청의 세션에 사용자를 넣는다.
	 *
	 * @param request
	 * @param username
	 */
	public void setSessionUser(HttpServletRequest request, String username) {
		User user = userService.getUserByUsername(username);
		if (user != null) {
			log.debug("User '" + username + "' into session");
			request.getSession().setAttribute(Constants.SESSION_KEY, user);
		}
	}

	/**
	 * 현재 쓰레드에 연결된 요청의 세션에서 사용자를 꺼낸다.
	 */
	public User getSessionUser() {
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder
				.currentRequestAttributes()).getRequest();
		return getSessionUser(request);
	}

	/**
	 * 요청의 세션에서 사용자를 꺼낸다.
	 *
	 * @param request
	 */
	public User getSessionUser(HttpServletRequest request) {
		return (User) request.getSession().getAttribute(Constants.SESSION_KEY);
	}
}
